﻿using System;
using System.Web.Mvc;
using System.Web.Routing;

using Fuse8.DomainFramework.Common;

namespace Fuse8.ContentManagementFramework.Web.ApplicationRuntime
{
    /// <summary>
    /// Just flag interface
    /// </summary>
    public interface ICmsRoute
    { }

    public interface INamedRoute
    {
        string Name { get; }
    }

    /// <summary>
    /// Just flag interface
    /// </summary>
    public interface ICmsAdministrationRoute
    { }

    public interface ICmsCustomAdministrationRoute
    { }

    public static class CmsRouteExtensions
    {
        public static void CmsAdministrationIgnoreRoute(this RouteCollection routes, string url)
        {
            Guard.ArgumentNotNull(routes, "routes");
            Guard.ArgumentNotEmpty(url, "url");

            routes.Add(new CmsAdministrationIgnoreRoute(url));
        }

        #region CMS Administartion Routes

        public static Route CmsAdministrationMapRoute(this RouteCollection routes, string name, string url, object defaults)
        {
            return CmsAdministrationMapRoute(routes, name, url, defaults, null);
        }

        public static Route CmsAdministrationMapRoute(this RouteCollection routes, string name, string url, object defaults, object constraints)
        {
            return CmsAdministrationMapRoute(routes, name, url, defaults, constraints, null);
        }

        public static Route CmsAdministrationMapRoute(this RouteCollection routes, string name, string url, object defaults, object constraints, string[] namespaces)
        {
            Guard.ArgumentNotNull(routes, "routes");
            Guard.ArgumentNotEmpty(name, "name");
            Guard.ArgumentNotEmpty(url, "url");

            return AdministrationMapRoute(routes, name, url, defaults, constraints, namespaces);
        }

        private static Route AdministrationMapRoute(RouteCollection routes, string name, string url, object defaults, object constraints, string[] namespaces, bool isCustomAdmin = false)
        {
            Route result = null;

            if (isCustomAdmin)
            {
                result = new CmsCustomAdministrationRoute(url, new MvcRouteHandler())
                {
                    Defaults = new RouteValueDictionary(defaults),
                    Constraints = new RouteValueDictionary(constraints),
                    DataTokens = new RouteValueDictionary(),
                    Name = name
                };                
            }
            else
            {
                result = new CmsAdministrationRoute(url, new MvcRouteHandler())
                {
                    Defaults = new RouteValueDictionary(defaults),
                    Constraints = new RouteValueDictionary(constraints),
                    DataTokens = new RouteValueDictionary(),
                    Name = name
                };
            }

            if ((namespaces != null) && (namespaces.Length > 0))
            {
                result.DataTokens["Namespaces"] = namespaces;
            }
            routes.Add(name, result);

            return result;
        }

        public static Route CmsAdministrationMapRoute(this AreaRegistrationContext context, string name, string url, object defaults)
        {
            return CmsAdministrationMapRoute(context, name, url, defaults, null);
        }

        public static Route CmsAdministrationMapRoute(this AreaRegistrationContext context, string name, string url, object defaults, object constraints)
        {
            return CmsAdministrationMapRoute(context, name, url, defaults, constraints, null);
        }

        public static Route CmsAdministrationMapRoute(this AreaRegistrationContext context, string name, string url, object defaults, object constraints, string[] namespaces)            
        {
            var route = context.Routes.CmsAdministrationMapRoute(name, url, defaults, constraints, namespaces);

            route.DataTokens["area"] = context.AreaName;
            route.DataTokens["UseNamespaceFallback"] = (namespaces == null || namespaces.Length == 0);

            return route;
        }

        #endregion

        #region CMS  Custom Administartion Routes

        public static Route CmsCustomAdministrationMapRoute(this RouteCollection routes, string name, string url, object defaults)
        {
            return CmsCustomAdministrationMapRoute(routes, name, url, defaults, null);
        }

        public static Route CmsCustomAdministrationMapRoute(this RouteCollection routes, string name, string url, object defaults, object constraints)
        {
            return CmsCustomAdministrationMapRoute(routes, name, url, defaults, constraints, null);
        }

        public static Route CmsCustomAdministrationMapRoute(this RouteCollection routes, string name, string url, object defaults, object constraints, string[] namespaces)
        {
            Guard.ArgumentNotNull(routes, "routes");
            Guard.ArgumentNotEmpty(name, "name");
            Guard.ArgumentNotEmpty(url, "url");

            return AdministrationMapRoute(routes, name, url, defaults, constraints, namespaces, true);
        }

        public static Route CmsCustomAdministrationMapRoute(this AreaRegistrationContext context, string name, string url, object defaults)
        {
            return CmsCustomAdministrationMapRoute(context, name, url, defaults, null);
        }

        public static Route CmsCustomAdministrationMapRoute(this AreaRegistrationContext context, string name, string url, object defaults, object constraints)
        {
            return CmsCustomAdministrationMapRoute(context, name, url, defaults, constraints, null);
        }

        public static Route CmsCustomAdministrationMapRoute(this AreaRegistrationContext context, string name, string url, object defaults, object constraints, string[] namespaces)
        {
            var route = CmsCustomAdministrationMapRoute(context.Routes, name, url, defaults, constraints, namespaces);

            route.DataTokens["area"] = context.AreaName;
            route.DataTokens["UseNamespaceFallback"] = (namespaces == null || namespaces.Length == 0);

            return route;
        }

        #endregion

        #region Non CMS Routes

        public static Route NonCmsMapRoute(this RouteCollection routes, string name, string url, object defaults)
        {
            return NonCmsMapRoute(routes, name, url, defaults, null);
        }

        public static Route NonCmsMapRoute(this RouteCollection routes, string name, string url, object defaults, string[] namespaces)
        {
            return NonCmsMapRoute(routes, name, url, defaults, null, namespaces);
        }

        public static Route NonCmsMapRoute(this RouteCollection routes, string name, string url, object defaults, object constraints)
        {
            return NonCmsMapRoute(routes, name, url, defaults, constraints, null);
        }

        public static Route NonCmsMapRoute(this RouteCollection routes, string name, string url, object defaults, object constraints, string[] namespaces)
        {
            Guard.ArgumentNotNull(routes, "routes");
            Guard.ArgumentNotEmpty(name, "name");
            Guard.ArgumentNotEmpty(url, "url");

            var route = new NonCmsRoute(url, new MvcRouteHandler())
            {
                Defaults = new RouteValueDictionary(defaults),
                Constraints = new RouteValueDictionary(constraints),
                DataTokens = new RouteValueDictionary(),
                Name = name
            };

            if ((namespaces != null) && (namespaces.Length > 0))
            {
                route.DataTokens["Namespaces"] = namespaces;
            }

            routes.Add(name, route);

            return route;
        }

        public static Route NonCmsMapRoute(this AreaRegistrationContext context, string name, string url, object defaults)
        {
            return NonCmsMapRoute(context, name, url, defaults, null);
        }

        public static Route NonCmsMapRoute(this AreaRegistrationContext context, string name, string url, object defaults, object constraints)
        {
            return NonCmsMapRoute(context, name, url, defaults, constraints, null);
        }

        public static Route NonCmsMapRoute(this AreaRegistrationContext context, string name, string url, object defaults, string[] namespaces)
        {
            return NonCmsMapRoute(context, name, url, defaults, null, namespaces);
        }

        public static Route NonCmsMapRoute(this AreaRegistrationContext context, string name, string url, object defaults, object constraints, string[] namespaces)
        {
            var route = context.Routes.NonCmsMapRoute(name, url, defaults, constraints, namespaces);

            route.DataTokens["area"] = context.AreaName;
            route.DataTokens["UseNamespaceFallback"] = (namespaces == null || namespaces.Length == 0);

            return route;
        }

        #endregion
    }

    public class CmsAdministrationRoute : Route, ICmsAdministrationRoute, INamedRoute
    {
        public CmsAdministrationRoute(string url, IRouteHandler routeHandler)
            : base(url, routeHandler)
        { }

        public string Name { get; set; }
    }

    public class CmsCustomAdministrationRoute : Route, ICmsCustomAdministrationRoute, INamedRoute
    {
        public CmsCustomAdministrationRoute(string url, IRouteHandler routeHandler)
            : base(url, routeHandler)
        { }

        public string Name { get; set; }
    }

    public class NonCmsRoute : Route, INamedRoute
    {
        public NonCmsRoute(string url, IRouteHandler routeHandler)
            : base(url, routeHandler)
        { }

        public string Name { get; set; }
    }

    public class CmsAdministrationIgnoreRoute : Route, ICmsAdministrationRoute
    {
        public CmsAdministrationIgnoreRoute(string url)
            : base(url, new StopRoutingHandler())
        { }

        public override VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary routeValues)
        {
            return null;
        }
    }
}
